﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Proggy.Data.Abstract;
using Proggy.Data.Entities;

namespace Proggy.Data.Concrete
{
    /// <summary>
    /// Memberhsip data repo
    /// </summary>
    public class MembershipDataRepository : IMembershipDataRepository
    {
        /// <summary>
        /// Constructor
        /// </summary>
        public MembershipDataRepository()
        {
            this._dc = new ProggyDataContext();
        }

        /// <summary>
        /// The data context
        /// </summary>
        ProggyDataContext _dc;

        /// <summary>
        /// The groups
        /// </summary>
        public IQueryable<Group> Groups
        {
            get
            {
                return this._dc.Groups.AsQueryable();
            }
        }

        /// <summary>
        /// Group members
        /// </summary>
        public IQueryable<GroupMember> GroupMembers
        {
            get
            {
                return this._dc.GroupMembers.AsQueryable();
            }
        }

        /// <summary>
        /// Get groups for member
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        public IEnumerable<Group> GetGroupsForMember(string username)
        {
            return this
                .GroupMembers
                .Where(x =>
                    x.Username.ToLower() == username.ToLower())
                .Select(x => x.Group);
        }

        /// <summary>
        /// Returns FormsAuthenticationMember or null
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        public FormsAuthenticationMember GetFormsAuthenticationMemberFromUsername(string username, bool activeOnly)
        {
            if (activeOnly)
            {
                return this
                    ._dc.FormsAuthenticationMembers
                    .SingleOrDefault(x =>
                        x.Username.ToLower() == username.ToLower() &&
                        x.IsActive
                    );
            }
            else
            {
                return this
                    ._dc.FormsAuthenticationMembers
                    .SingleOrDefault(x =>
                        x.Username.ToLower() == username.ToLower()
                    );
            }
        }

        /// <summary>
        /// Returns all forms authentication members (use sparingly)
        /// </summary>
        /// <returns></returns>
        public IQueryable<FormsAuthenticationMember> GetAllFormsAuthenticationMembers()
        {
            return this._dc.FormsAuthenticationMembers.AsQueryable();
        }

        /// <summary>
        /// Change password
        /// </summary>
        /// <param name="currentUsername"></param>
        /// <param name="newPassword"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public bool GetChangePasswordOk(string currentUsername, string currentPassword, string newPassword, ref string errorMsg)
        {
            try
            {
                // get member
                var member = this.GetFormsAuthenticationMemberFromUsername(currentUsername, true);

                // check
                if (member == null)
                {
                    errorMsg = "Member " + currentUsername + " does not exist";
                    return false;
                }

                // check current
                member.EnteredPassword = currentPassword;
                if (!member.ValidatePassword())
                {
                    errorMsg = "The current password does not match the saved value for user " + currentUsername;
                    return false;
                }

                // do checks
                if (!this.PasswordIsValid(newPassword, ref errorMsg))
                {
                    return false;
                }

                // update
                if (!this.UpdatePasswordOk(member, newPassword, ref errorMsg))
                {
                    return false;
                }


            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
                return false;
            }

            return true;
        }

        /// <summary>
        /// Check validity of password
        /// </summary>
        /// <param name="password"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public bool PasswordIsValid(string password, ref string errorMsg)
        {

            if (password.Contains(' ') || password.Length < FormsAuthenticationMember.MIN_PASSWORD_LENGTH || password.Length > FormsAuthenticationMember.MAX_PASSWORD_LENGTH)
            {
                errorMsg = "Password cannot be less then 6 or more than 64 characters long and cannot contain spaces";
                return false;
            }

            return true;

        }

        /// <summary>
        /// Try update password
        /// </summary>
        /// <param name="member"></param>
        /// <param name="newPassword"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public bool UpdatePasswordOk(FormsAuthenticationMember member, string newPassword, ref string errorMsg)
        {

            try
            {
                // set
                member.EnteredPassword = newPassword;
                member.SetHash();

                // save
                this._dc.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
                return false;
            }

        }

        /// <summary>
        /// Sets last login
        /// </summary>
        /// <param name="member"></param>
        public void SetLastLogon(FormsAuthenticationMember member)
        {
            member.LastLogon = DateTime.Now;
            this._dc.SaveChanges();
        }

        /// <summary>
        /// Register new member
        /// </summary>
        /// <param name="saveData">If false, no data will be saved (use as a check)</param>
        /// <param name="name"></param>
        /// <param name="username"></param>
        /// <param name="email"></param>
        /// <param name="password"></param>
        /// <param name="guid"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public bool GetRegisterNewMemberOk(bool saveData, string name, string username, string email, string password, Guid guid, ref string errorMsg)
        {
            try
            {
                // check strings
                if (name == null) name = "";
                if (username == null) username = "";
                if (email == null) email = "";
                if (password == null) password = "";

                // trim
                name = name.Trim();
                username = username.Trim();
                password = password.Trim();
                email = email.Trim();

                // check name
                if (name.Length < 2)
                {
                    errorMsg = "Name must be at least 2 characters long";
                    return false;
                }

                // check username
                if (username.Length < 1)
                {
                    errorMsg = "Please enter a username";
                    return false;
                }

                // check email
                if (!System.Text.RegularExpressions.Regex.IsMatch(email, @"^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$"))
                {
                    errorMsg = "Please enter a valid email address";
                    return false;
                }


                // check password
                if (!this.PasswordIsValid(password.Trim(), ref errorMsg))
                {
                    return false;
                }

                // check existing
                var existing =
                    this
                    ._dc
                    .FormsAuthenticationMembers
                    .Where(x =>
                        x.Username.ToLower() == username.ToLower() ||
                        x.Email.ToLower() == email.ToLower())
                    .Count() > 0;

                //
                if (existing)
                {
                    errorMsg = "Chosen details are not unique - either username or email is already registered on this site.";
                    return false;
                }

                // everything ok, so add
                var newMember = new FormsAuthenticationMember
                {
                    ActivationCode = "ACTIVATION_" + guid.ToString(),
                    Created = DateTime.Now,
                    Email = email.ToLower(),
                    EnteredPassword = password,
                    IsActive = false,
                    Username = username,
                    Name = name
                };

                // set hash
                newMember.SetHash();

                if (saveData)
                {

                    // add
                    this._dc.FormsAuthenticationMembers.Add(newMember);

                    // save
                    this._dc.SaveChanges();
                }

                //
                return true;


            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
                return false;
            }
        }

        /// <summary>
        /// Activate membership
        /// </summary>
        /// <param name="activationCode"></param>
        /// <returns></returns>
        public void ActivateMembership(string activationCode)
        {
            try
            {
                // find member
                if (!string.IsNullOrEmpty(activationCode))
                {
                    //
                    var member = this._dc.FormsAuthenticationMembers.SingleOrDefault(x => x.ActivationCode == activationCode);

                    // check
                    if (member != null && member.HasActivationGuidReady)
                    {
                        member.IsActive = true;
                        member.ActivationCode = null;
                        this._dc.SaveChanges();
                    }
                }
            }
            catch
            {
                // DO NOTHING HERE = NO NEED AS TESTED AND WORKS AND IF GUID WRONG THEN DON'T WANT ERROR DISPLAYING
            }
        }

        /// <summary>
        /// Reset password
        /// </summary>
        /// <param name="emailAddress"></param>
        /// <param name="newPassword"></param>
        /// <param name="username"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public bool GetResetPasswordOk(string emailAddress, string newPassword, ref string username, ref string errorMsg)
        {
            try
            {
                // check email
                if (string.IsNullOrEmpty(emailAddress))
                {
                    errorMsg = "Please enter your email address";
                    return false;
                }

                // set email
                emailAddress = emailAddress.ToLower().Trim();

                // find
                var member = this.GetMemberByEmailAddress(emailAddress);

                // check
                if (member == null)
                {
                    errorMsg = "Could not find a member with that email address";
                    return false;
                }

                // set
                member.EnteredPassword = newPassword;
                member.SetHash();

                // save
                this._dc.SaveChanges();

                // set username
                username = member.Username;

                //
                return true;
            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
                return false;
            }

        }

        /// <summary>
        /// Returns member
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public FormsAuthenticationMember GetMemberByEmailAddress(string email)
        {
            return
                string.IsNullOrEmpty(email) || string.IsNullOrEmpty(email.Trim()) ?
                null
                :
                this._dc.FormsAuthenticationMembers.SingleOrDefault(x => x.Email.ToLower() == email.Trim().ToLower());
        }

        /// <summary>
        /// Forms members
        /// </summary>
        public IQueryable<FormsAuthenticationMember> FormsAuthenticationMembers
        {
            get { return this._dc.FormsAuthenticationMembers.AsQueryable(); }
        }

        /// <summary>
        /// Add member to group
        /// </summary>
        /// <param name="group"></param>
        /// <param name="memb"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public bool AddMemberToGroup(Group group, string memb, string currentUsername, ref string errorMsg)
        {
            try
            {
                // find group
                var group_ = this._dc.Groups.Find(group.GroupId);

                //
                //
                if (group_ != null)
                {
                    // check
                    if (!(group_.GroupMembers.Select(x => x.Username).Contains(memb)))
                    {

                        // add
                        group_.GroupMembers.Add(new GroupMember
                        {
                            Username = memb,
                            Group = group_,
                            Created = DateTime.Now,
                            CreatedUsername = currentUsername

                        });

                        // save
                        this._dc.SaveChanges();
                    }
                }

                //
                return true;
            }

            catch (Exception ex)
            {
                errorMsg = ex.Message;
                return false;
            }
        }

        /// <summary>
        /// Remove group member
        /// </summary>
        /// <param name="groupMemberId"></param>
        public void RemoveGroupMember(int groupMemberId)
        {
            // find
            var groupMember = this._dc.GroupMembers.SingleOrDefault(x => x.GroupMemberId == groupMemberId);

            //
            if (groupMember != null)
            {
                this._dc.GroupMembers.Remove(groupMember);
                this._dc.SaveChanges();
            }
        }

        /// <summary>
        /// Add group
        /// </summary>
        /// <param name="groupName"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public bool AddGroup(string groupName, string currentUsername, ref string errorMsg)
        {
            // trim
            if (groupName == null) groupName = "";
            if (!string.IsNullOrEmpty(groupName)) groupName = groupName.Trim();

            // check empty
            if (string.IsNullOrEmpty(groupName))
            {
                errorMsg = "Group name cannot be empty";
                return false;
            }

            //*** do checks ****//
            var isDuplicate = this.Groups.Where(x => x.GroupName.ToLower() == groupName.ToLower()).Count() > 0;

            //
            if (isDuplicate)
            {
                errorMsg = "This group name already exists";
                return false;
            }

            // add
            try
            {
                //
                var now = DateTime.Now;

                this._dc.Groups.Add(new Group
                {
                    GroupName = groupName,
                    FirstCreated = now,
                    LastUpdated = now,
                    FirstCreatedUsername = currentUsername,
                    LastUpdatedUsername = currentUsername

                });
                this._dc.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
                return false;
            }
        }
    }
}
